package com.yyt.yytcodesandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import com.yyt.yytcodesandbox.model.ExecuteCodeRequest;
import com.yyt.yytcodesandbox.model.ExecuteCodeResponce;
import com.yyt.yytcodesandbox.model.ExecuteMessage;
import com.yyt.yytcodesandbox.model.JudgeInfo;
import com.yyt.yytcodesandbox.utils.ProcessUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;


public abstract class JavaCodeSandboxTemplate implements CodeSandbox{

    private static final String GLOBAL_CODE_DIR_NAME="tmpCode";
    private static final String GLOBAL_CODE_CLASS_NAME="Main.java";
    private static final Long TIME_LIMIT=10000L;
    private static final List<String> blackList=Arrays.asList("Files","exec");
    private static final String SECURITY_MANAGER_PATH="D:\\work-code\\oj\\yyt-code-sandbox\\src\\main\\resources\\security";
    private static final String SECURITY_CLASS_NAME="DefaultSecurityManager";
    private static final WordTree WORD_TREE;
    static {
        WORD_TREE=new WordTree();
        WORD_TREE.addWords(blackList);
    }

    @Override
    public ExecuteCodeResponce executeCode(ExecuteCodeRequest executeCodeRequest) {
        //1、把用户的代码保存为文件
        File userCodeFile=saveCodeToFile(executeCodeRequest);

        //2、编译代码，得到class文件，获得编译输出信息
        ExecuteMessage executeMessage = compileFile(userCodeFile);
        System.out.println(executeMessage);

        //3、执行文件，得到输出结果————每一个用例都要执行
        List<ExecuteMessage> executeMessageList = runFile(userCodeFile, executeCodeRequest);

        //4、收集整理输出结果
        ExecuteCodeResponce executeCodeResponce = getOutputResponce(executeMessageList);

        //5、文件清理
        deleteCodeFile(userCodeFile);

        //6、错误处理，提升程序健壮性——getErrorResponce
        return executeCodeResponce;
    }


    public File saveCodeToFile(ExecuteCodeRequest executeCodeRequest){
        String code= executeCodeRequest.getCode();

        String userDir = System.getProperty("user.dir");
        String globalCodePathName=userDir+ File.separator+GLOBAL_CODE_DIR_NAME;
        if(!FileUtil.exist(globalCodePathName)){
            FileUtil.mkdir(globalCodePathName);
        }
        //每份代码独立存储
        String userCodeParentPath=globalCodePathName+File.separator+ UUID.randomUUID();
        String userCodePath=userCodeParentPath+File.separator+GLOBAL_CODE_CLASS_NAME;
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
        return userCodeFile;
    }

    public ExecuteMessage compileFile(File userCodeFile){
        String compileCmd=String.format("javac -encoding utf-8 %s",userCodeFile.getAbsolutePath());
        try {
            Process compileProcess = Runtime.getRuntime().exec(compileCmd);
            ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(compileProcess, "编译");
            System.out.println(executeMessage);
            return executeMessage;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public List<ExecuteMessage> runFile(File userCodeFile,ExecuteCodeRequest executeCodeRequest){
        String userCodeParentPath=userCodeFile.getParent();
        List<String> inputList = executeCodeRequest.getInputList();
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for(int i=0;i< inputList.size();i++){
            String inputArgs=inputList.get(i);
            //为了避免占用资源，给jvm分配最大内存
            //运行时启动java安全管理器
//            String runCmd=String.format("java -Xmx256m -Dfile.encoding=utf-8 -cp %s;%s -Djava.security.manager=%s Main %s",userCodeParentPath,SECURITY_MANAGER_PATH,SECURITY_CLASS_NAME,inputArgs);
            String runCmd=String.format("java -Xmx256m -Dfile.encoding=utf-8 -cp %s Main %s",userCodeParentPath,inputArgs);
            try {
                Process runProcess = Runtime.getRuntime().exec(runCmd);
                //设置保护线程，避免时间阻塞
                new Thread(()->{
                    try {
                        Thread.sleep(TIME_LIMIT);
                        runProcess.destroy();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).start();
                ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(runProcess, "运行");
                executeMessageList.add(executeMessage);
                System.out.println(executeMessage);

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return executeMessageList;
    }

    public ExecuteCodeResponce getOutputResponce(List<ExecuteMessage> executeMessageList){
        ExecuteCodeResponce executeCodeResponce = new ExecuteCodeResponce();
        List<String> outputList=new ArrayList<>();
        long maxTime=0;
        for(ExecuteMessage executemessage:executeMessageList){
            Integer exitValue = executemessage.getExitValue();
            Long time = executemessage.getTime();
            String message = executemessage.getMessage();
            String errorMessage = executemessage.getErrorMessage();
            if(!StrUtil.isBlank(errorMessage)){
                executeCodeResponce.setMessage(errorMessage);
                //用户提交的代码存在错误，设置状态为3
                executeCodeResponce.setStatus(3);
                break;
            }
            outputList.add(message);
            //程序的执行时间为最大执行时间
            if(time!=null){
                maxTime=Math.max(maxTime,time);
            }
        }
        //遍历完所有的executemessage||遇到error的情况退出了
        if(outputList.size()==executeMessageList.size()){
            //正常运行完
            executeCodeResponce.setStatus(1);
        }
        executeCodeResponce.setOutputList(outputList);
        JudgeInfo judgeInfo=new JudgeInfo();
        judgeInfo.setTime(maxTime);
        executeCodeResponce.setJudgeInfo(judgeInfo);
        return executeCodeResponce;
    }

    public void deleteCodeFile(File userCodeFile){
        String userCodeParentPath=userCodeFile.getParent();
        if(userCodeFile.getParentFile()!=null){
            boolean del = FileUtil.del(userCodeParentPath);
            System.out.println("删除"+(del?"成功":"失败"));
        }
    }

    private ExecuteCodeResponce getErrorResponce(Exception e,File userCodeFile){
        if(userCodeFile.getParentFile()!=null){
            boolean del = FileUtil.del(userCodeFile.getParentFile());
            System.out.println("删除"+(del?"成功":"失败"));
        }
        ExecuteCodeResponce executeCodeResponce=new ExecuteCodeResponce();
        executeCodeResponce.setOutputList(new ArrayList<>());
        executeCodeResponce.setMessage(e.getMessage());
        //2表示代码沙箱错误
        executeCodeResponce.setStatus(1);
        executeCodeResponce.setJudgeInfo(new JudgeInfo());
        return executeCodeResponce;
    }
}
